package session

import (
	"context"
	"errors"
	"fmt"
	"github.com/go-redis/redis/v8"
	"github.com/valyala/fasthttp"
	"strings"
	"time"
)

var (
	prefix string
	ctx    = context.Background()
	client *redis.Client
)

func New(reddb *redis.Client, p string) {

	client = reddb
	prefix = p
}

func AdminSet(value []byte, uid, deviceNo string) (string, error) {

	uuid := fmt.Sprintf("TD%s", uid)
	key := fmt.Sprintf("%d", Cputicks())

	val, err := client.Get(ctx, uuid).Result()

	pipe := client.Pipeline()
	defer pipe.Close()

	if err != redis.Nil && len(val) > 0 {
		//同一个用户，一个时间段，只能登录一个
		results := strings.SplitN(val, ",", 3)
		pipe.Unlink(ctx, results[1])
	}

	v := fmt.Sprintf("%s,%s", deviceNo, key)
	pipe.Set(ctx, uuid, v, 2*time.Hour)
	pipe.SetNX(ctx, key, value, 2*time.Hour)

	_, err = pipe.Exec(ctx)

	return key, err
}

func Set(value []byte, uid string) (string, error) {

	uuid := fmt.Sprintf("TI%s", uid)
	key := fmt.Sprintf("%s:%d", prefix, Cputicks())

	val, err := client.Get(ctx, uuid).Result()

	pipe := client.Pipeline()
	defer pipe.Close()

	if err != redis.Nil && len(val) > 0 {
		//同一个用户，一个时间段，只能登录一个
		pipe.Unlink(ctx, val)
	}

	pipe.Set(ctx, uuid, key, defaultExpires)
	pipe.SetNX(ctx, key, value, defaultExpires)
	_, err = pipe.Exec(ctx)

	return key, err
}

func Update(value []byte, uid uint64) bool {

	uuid := fmt.Sprintf("TI%d", uid)

	val := client.Get(ctx, uuid).Val()
	pipe := client.Pipeline()
	defer pipe.Close()

	if len(val) == 0 {
		return false
	}

	pipe.Unlink(ctx, val)
	pipe.SetNX(ctx, val, value, renewExpires)

	_, err := pipe.Exec(ctx)
	if err != nil {
		return false
	}

	return true
}

func Offline(uids []string) error {

	var (
		cmds  []*redis.StringCmd
		uKeys []string
	)
	pipe1 := client.Pipeline()
	for _, v := range uids {
		uuid := fmt.Sprintf("TI%s", v)
		cmds = append(cmds, pipe1.Get(ctx, uuid))
	}
	_, err := pipe1.Exec(ctx)
	_ = pipe1.Close()
	if err != nil {
		return err
	}

	for _, cmd := range cmds {
		uKey, err := cmd.Result()
		if err == nil {
			uKeys = append(uKeys, uKey)
		}
	}

	pipe2 := client.Pipeline()
	for _, v := range uKeys {
		pipe2.Unlink(ctx, v)
	}
	_, err = pipe2.Exec(ctx)
	_ = pipe2.Close()
	return err
}

func Destroy(ctx *fasthttp.RequestCtx) {

	key := string(ctx.Request.Header.Peek("t"))
	if len(key) == 0 {
		return
	}

	client.Unlink(ctx, key)
}

func Get(ctx *fasthttp.RequestCtx) ([]byte, error) {

	key := string(ctx.Request.Header.Peek("t"))
	if len(key) == 0 {

		key = string(ctx.QueryArgs().Peek("t"))
		if len(key) == 0 {
			return nil, errors.New("does not exist")
		}
	}

	pipe := client.Pipeline()
	defer pipe.Close()

	data := pipe.Get(ctx, key)
	_ = pipe.ExpireAt(ctx, key, ctx.Time().Add(defaultExpires))
	pipe.Exec(ctx)

	val, err := data.Bytes()
	if err != nil {
		return nil, err
	} else {
		return val, nil
	}
}

func AdminGet(ctx *fasthttp.RequestCtx) ([]byte, error) {

	key := string(ctx.Request.Header.Peek("t"))
	if len(key) == 0 {

		key = string(ctx.QueryArgs().Peek("t"))
		if len(key) == 0 {
			return nil, errors.New("does not exist")
		}
	}

	pipe := client.Pipeline()
	defer pipe.Close()

	data := pipe.Get(ctx, key)
	_ = pipe.ExpireAt(ctx, key, ctx.Time().Add(2*time.Hour))
	pipe.Exec(ctx)

	val, err := data.Bytes()
	if err != nil {
		return nil, err
	} else {
		return val, nil
	}
}
